#ifndef     __DECODER_H
#define     __DECODER_H


extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
}
#include "../utils/error.h"
#include "../../utils/nocopyable.h"

class Decoder : private utils::NoCopyable {

public:
    Decoder(AVStream *stream) {
        m_stream = stream;
        AVCodecParameters *par = stream->codecpar;

        const AVCodec *codec = avcodec_find_decoder(par->codec_id);
        if (codec == nullptr) {
            throw FFmpegException("avcodec_find_decoder codec == nullptr");
        }

        m_ctx = avcodec_alloc_context3(codec);
        if (m_ctx == nullptr) {
            throw FFmpegException("avcodec_alloc_context3 ctx == nullptr");
        }

        int ec = avcodec_parameters_to_context(m_ctx, par);
        if (ec < 0) {
            avcodec_free_context(&m_ctx);
            throw FFmpegException("avcodec_parameters_to_context", ec);
        }

        ec = avcodec_open2(m_ctx, codec, nullptr);
        if (ec < 0) {
            avcodec_free_context(&m_ctx);
            throw FFmpegException("avcodec_open2", ec);
        }
    }

    ~Decoder() {
        avcodec_close(m_ctx);
        avcodec_free_context(&m_ctx);
    }

    bool send_packet(const AVPacket *packet) {
        int ec = avcodec_send_packet(m_ctx, packet);
        switch (ec) {
            case AVERROR_EOF:
            case AVERROR(EAGAIN):
                return false;
            case 0:
                return true;
        }
        throw FFmpegException("send_packet", ec);
    }

    bool receive_frame(AVFrame *frame) {
        int ec = avcodec_receive_frame(m_ctx, frame);
        switch (ec) {
        case AVERROR_EOF:
        case AVERROR(EAGAIN):
                return false;
        case 0:
                return true;
        }
        throw FFmpegException("avcodec_receive_frame", ec);
    }

    AVRational time_base() const {
        return m_stream->time_base;
    }

protected:
    AVCodecContext *m_ctx;
    AVStream *m_stream;
};


#endif
